package com.davfx.httpnio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.HashMap;
import java.util.Map;

public final class HttpServer {

	public HttpServer(Worker worker, int port, final HttpRequestHandler handler) throws IOException {
		Listener listener = new Listener(worker);
		Listening listening = listener.listen(Http.LOCALHOST, port);
		listening.listen(new ListenConnectionHandler(new HttpListenFactory() {
			@Override
			public void failed(IOException e) {
				handler.failed(e);
			}
			@Override
			public HttpReadListenHandler create() {
				return new HttpReadListenHandler() {
					private HttpPostHandler postHandler;
					
					@Override
					public void handle(ByteBuffer buffer) {
						postHandler.handle(buffer);
					}
					
					@Override
					public void handle(String clientHost, int clientPort, HttpRequest.Method method, String path, Map<String, String> headers) {
						Map<String, String> parameters = new HashMap<String, String>();
						int i = path.indexOf(Http.PARAMETERS_START);
						if (i >= 0) {
							String[] params = path.substring(i + 1).split("\\" + Http.PARAMETERS_SEPARATOR);
							for (String p : params) {
								String[] kv = p.split("\\" + Http.PARAMETER_KEY_VALUE_SEPARATOR);
								if (kv.length == 1) {
									parameters.put(Http.Url.decode(kv[0]), "");
								} else {
									parameters.put(Http.Url.decode(kv[0]), Http.Url.decode(kv[1]));
								}
							}
							path = path.substring(0, i);
						}
						HttpRequest request = new HttpRequest(clientHost, clientPort, method, path, headers, parameters);
						postHandler = handler.handle(request);
					}
					
					@Override
					public void failed(IOException e) {
						postHandler.failed(e);
					}
					
					@Override
					public void ended(final ResponseWriter responseWriter) {
						postHandler.write(new Response() {
							@Override
							public void write(ByteBuffer buffer) {
								responseWriter.write(buffer);
							}
							@Override
							public void write(HttpResponse response) {
								responseWriter.write(response.getStatus(), response.getReason(), response.getHeaders());
							}
							@Override
							public void close() {
								responseWriter.close();
							}
						});
					}
					
					@Override
					public void closed() {
						postHandler.closed();
					}
				};
			}
		}));
	}
	
	public static interface Response {
		void write(HttpResponse response);
		void write(ByteBuffer buffer);
		void close();
	}
	
	public static interface HttpBasicHandler {
		void write(Map<String, String> postParameters, Response response);
		void closed();
	}
	
	public static interface HttpPostHandler {
		void write(Response response);
		void closed();
		void handle(ByteBuffer buffer);
		void failed(IOException e);
	}
	
	public static final class HttpPostRequestHandler implements HttpPostHandler {
		private final HttpBasicHandler handler;
		private final StringBuilder post = new StringBuilder();
		public HttpPostRequestHandler(HttpBasicHandler handler) {
			this.handler = handler;
		}
			
		@Override
		public void write(Response response) {
			Map<String, String> parameters = new HashMap<String, String>();
			if (post.length() > 0) {
				String postContent = post.toString();
				String[] params = postContent.split("\\" + Http.PARAMETERS_SEPARATOR);
				for (String p : params) {
					String[] kv = p.split("\\" + Http.PARAMETER_KEY_VALUE_SEPARATOR);
					if (kv.length == 1) {
						parameters.put(Http.Url.decode(kv[0]), "");
					} else {
						parameters.put(Http.Url.decode(kv[0]), Http.Url.decode(kv[1]));
					}
				}
			}
			handler.write(parameters, response);
		}
		
		@Override
		public void handle(ByteBuffer buffer) {
			post.append(new String(buffer.array(), buffer.position(), buffer.remaining()));
			buffer.position(buffer.limit());
		}
		
		@Override
		public void failed(IOException e) {
			handler.closed();
		}
		
		@Override
		public void closed() {
			handler.closed();
		}
	}
	
	public static interface HttpRequestHandler {
		HttpPostHandler handle(HttpRequest request);
		void failed(IOException e);
	}
	
	public static final class TextResponse {
		private final Charset charset;
		private final CharsetEncoder encoder;
		private final Response response;
		public TextResponse(String acceptCharset, Response response) {
			this.response = response;
			
			if (acceptCharset != null) {
				Charset cs = null;
				double q = 0;
				String[] s = acceptCharset.split("\\" + Http.MULTIPLE_SEPARATOR);
				for (String t : s) {
					String[] c = t.split("\\" + Http.EXTENSION_SEPARATOR);
					double cq = 1;
					if (c.length == 2) {
						String ext = c[1];
						if (ext.startsWith(Http.QUALITY + Http.PARAMETER_KEY_VALUE_SEPARATOR)) {
							try {
								cq = Double.parseDouble(ext.substring(Http.QUALITY.length() + 1));
							} catch (NumberFormatException e) {
							}
						}
					}
					if (cq > q) {
						String cc = c[1];
						if (cc.equals(Http.WILDCARD)) {
							cs = Http.DEFAULT_CHARSET;
						} else if (Charset.isSupported(cc)) {
							cs = Charset.forName(cc);
						}
						q = cq;
					}
				}
				if (cs == null) {
					charset = Http.DEFAULT_CHARSET;
				} else {
					charset = cs;
				}
			} else {
				charset = Http.UTF8_CHARSET;
			}

			encoder = charset.newEncoder();
		}
		public void write(HttpResponse r, String content) {
			ByteBuffer b;
			try {
				b = encoder.encode(CharBuffer.wrap(content));
			} catch (CharacterCodingException e) {
				throw new RuntimeException(e);
			}
			r.getHeaders().put(Http.CONTENT_LENGTH, String.valueOf(b.remaining()));
			response.write(r);
			response.write(b);
		}
	}
}
